<?php

class CW_MountClass {

   /**
	* Core registry
	* @access Protected
	* @var Object
	*/
	protected $Reg;


   // ! Constructor Method

   /**
	* Instantiates class and defines instance variables.
	*
	* @param Object $Reg Core registry passed by reference
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access public
	* @return Void
	*/
	public function __construct(&$Reg)
	{
		$this->Reg = &$Reg;
	}


   // ! Executor Method

   /**
	* Convenience method that allows a user to easily add
	* XML to the current schema.
	*
	* @param Object $Xml The xml to add
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Protected
	* @return True
	*/
	public function extension($extension)
	{
		$path_to_extension = PATH_EXTENSIONS . DIRECTORY_SEPARATOR . "{$extension}.extension" . PHP_EXT;

		if(false == file_exists($path_to_extension))
		{
			throw new CoreException("Cannot mount extension '{$extension}' as it does not exist within path '" . PATH_EXTENSIONS . "'!");
		}

		include_once($path_to_extension);

		return true;
	}


   // ! Executor Method

   /**
	* Convenience method that allows a user to easily add
	* XML to the current schema.
	*
	* @param Object $Xml The xml to add
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Protected
	* @return True
	*/
	public function model($model, $application = CURRENT_APPLICATION)
	{
		// So, have we already mounted this model:

		$model_class_name = NAMESPACE . "{$model}ModelClass";

		if(class_exists($model_class_name))
		{
			return new $model_class_name($this->Reg->DB);
		}


		// Sort some default names and paths out:

		$path_friendly_name = strtolower($model);
		$path_to_models = PATH_APPLICATION_MODELS;


		// Are we pulling from a different application? If so, let's see if the
		// application exists and modify the model path accordingly:

		if($application != CURRENT_APPLICATION)
		{
			if(false == $this->Reg->App->doesApplicationExist($application))
			{
				throw new CoreException("Cannot load model '{$model}'. The application '{$application}' does not exist!");
			}

			$path_to_models = PATH_APPS . $application . DIRECTORY_SEPARATOR . 'models' . DIRECTORY_SEPARATOR;
		}


		// Is this a valid model path:

		if(false == is_dir($path_to_models))
		{
			throw new CoreException("Cannot load model '{$model}'. The path to said model '{$path_to_models}' does not exist!");
		}


		// Does this model even exist within the file system:

		if(false == file_exists("{$path_to_models}{$path_friendly_name}.model" . PHP_EXT))
		{
			throw new CoreException("Model '{$model}' does not exist within model path '{$path_to_models}'.");
		}


		// Let's fetch the file and get ready to return it:

		require_once "{$path_to_models}{$path_friendly_name}.model" . PHP_EXT;

		if(false == class_exists($model_class_name))
		{
			throw new CoreException("Model '{$model_class_name}' could not be found. Make sure you got the spelling and case right.");
		}


		// Instantiate and return:

		return new $model_class_name($this->Reg->DB);
	}


   // ! Executor Method

   /**
	* Convenience method that allows a user to easily add
	* XML to the current schema.
	*
	* @param Object $Xml The xml to add
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Protected
	* @return True
	*/
	public function library($library, &$parameters = array(), $load_from_core = true, $application = CURRENT_APPLICATION)
	{
		$path_friendly_name = strtolower($library);

		if($load_from_core)
		{
			$path_to_library = PATH_CORE;

			if(false == file_exists($path_to_library. "{$path_friendly_name}.core" . PHP_EXT))
			{
				throw new CoreException("Cannot locate core library '{$library}'!");
			}

			$library_class_name = "CW_{$library}Class";
		}
		else
		{
			$path_to_library = PATH_APPLICATION_CORE;

			if($application != CURRENT_APPLICATION)
			{
				if(false == $this->Reg->App->doesApplicationExist($application))
				{
					throw new CoreException("Cannot load library '{$library}'. The application '{$application}' does not exist!");
				}

				$path_to_library = PATH_APPS . $application . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR;
			}

			$library_class_name = NAMESPACE . "{$library}Class";
		}

		if(false == is_dir($path_to_library))
		{
			throw new CoreException("Cannot load library '{$library}'. The path to said library '{$path_to_library}' does not exist!");
		}

		if(false == file_exists("{$path_to_library}{$path_friendly_name}.core" . PHP_EXT))
		{
			throw new CoreException("Library '{$library}' does not exist within core library path '{$path_to_library}'.");
		}

		if(class_exists($library_class_name) || $this->Reg->checkIfObjectExists($library))
		{
			return true;
		}

		require_once "{$path_to_library}{$path_friendly_name}.core" . PHP_EXT;

		if(false == class_exists($library_class_name))
		{
			throw new CoreException("Library '{$library_class_name}' could not be found. Make sure you got the spelling and case right.");
		}

		$Reflector = new ReflectionClass($library_class_name);

		$this->Reg->registerObject($library, $Reflector->newInstanceArgs(array($parameters)));

		return true;
	}


   // ! Executor Method

   /**
	* Convenience method that allows a user to easily add
	* XML to the current schema.
	*
	* @param Object $Xml The xml to add
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Protected
	* @return True
	*/
	public function factory($factory, $application = CURRENT_APPLICATION)
	{
		// Sort some default names and paths out:

		$path_friendly_name = strtolower($factory);
		$path_to_factories = PATH_APPLICATION_FACTORIES;
		$factory_class_name = NAMESPACE . "{$factory}XmlFactory";


		// Before we do anything else, check if we have the XmlElement class around somewhere:

		if(false == class_exists('XmlElement'))
		{
			throw new CoreException("Cannot load xml factory '{$factory}'. Factories must have access to class 'XmlElement'.");
		}

		// Did we already include this class? If so, let's get out of here:

		if(class_exists(NAMESPACE . "{$factory}XmlFactory"))
		{
			return new $factory_class_name;
		}


		// Are we pulling from a different application? If so, let's see if the
		// application exists and modify the factory path accordingly:

		if($application != CURRENT_APPLICATION)
		{
			if(false == $this->Reg->App->doesApplicationExist($application))
			{
				throw new CoreException("Cannot load xml factory '{$factory}'. The application '{$application}' does not exist!");
			}

			$path_to_factories = PATH_APPS . $application . DIRECTORY_SEPARATOR . 'factories' . DIRECTORY_SEPARATOR;
		}


		// Is this a valid factory path:

		if(false == is_dir($path_to_factories))
		{
			throw new CoreException("Cannot load xml factory '{$factory}'. The path to said factory '{$path_to_factories}' does not exist!");
		}


		// Does this factory even exist within the file system:

		if(false == file_exists("{$path_to_factories}{$path_friendly_name}.factory" . PHP_EXT))
		{
			throw new CoreException("Factory '{$factory}' does not exist within factory path '{$path_to_factories}'.");
		}


		// Let's fetch the file and get ready to return it:

		require_once "{$path_to_factories}{$path_friendly_name}.factory" . PHP_EXT;


		// Ok, do we have a naming convention issue?

		if(false == class_exists($factory_class_name))
		{
			throw new CoreException("Factory class '{$factory_class_name}' could not be found. Make sure you got the spelling and case right.");
		}


		// Instantiate and return:

		return new $factory_class_name;
	}


   /**
	* !!!EXPERIMENTAL!!!
	*
	* I may or may not keep this functionality within Cogsworth. The idea is
	* to create an interface between existing applications so that they can
	* interact with eachother's controllers. This way Application A can call any
	* controller method within Application B and vice versa. This is pretty
	* much the reason behind controller namespacing. PHP will freak out if you
	* are calling a foreign controller that has the same class name as the
	* controller you're calling from.
	*
	* At this moment in time, it is recommended that you use this functionality
	* only on controller methods that have been specifically written to use it.
	*
	* NOTE: Theoretically, you can link controllers within the same application
	* and utilize 100% of their functionality.
	*
	* @param String $application The application that houses the requested controller
	* @param String $controller Name of the requested controller
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access Protected
	* @return Object
	*/
	public function link($application, $controller)
	{
		$application = strtolower($application);
		$controller = strtolower($controller);

		$path_to_application = PATH_APPLICATION;
		$path_to_controller = PATH_APPLICATION_CONTROLLERS . "{$controller}.controller" . PHP_EXT;

		if($application == CURRENT_APPLICATION && $controller == CURRENT_CONTROLLER)
		{
			throw new CoreException("Not happening. You cannot link to the controller you are linking from.");
		}

		if($application != CURRENT_APPLICATION)
		{
			// Does this application exist?

			if(false == $this->Reg->App->doesApplicationExist($application))
			{
				throw new CoreException("Link to application '{$application}' could not be established. That application does not exist.");
			}


			// Does this application's requested controller exist?

			if(false == $this->Reg->App->doesControllerExist($application, $controller))
			{
				throw new CoreException("Link to application '{$application}' and controller '{$controller}' could not be established. That controller does not exist.");
			}


			// Get some paths established to the requested application:

			$path_to_application = PATH_APPS . $application . DIRECTORY_SEPARATOR;
			$path_to_controller = $path_to_application . 'controllers' . DIRECTORY_SEPARATOR . "{$controller}.controller" . PHP_EXT;


			// Load up the applications config file. We need the namespace value for its controllers:

			$this->Reg->Config->load($path_to_application . 'config' . DIRECTORY_SEPARATOR . 'config.php', $application);
		}


		// Does this controller's file exist?

		if(false == file_exists($path_to_controller))
		{
			throw new CoreException("Controller link has failed. The file for controller '{$controller}' could not be found.");
		}


		// Fetch the controller's file and generate the class name:

		include_once $path_to_controller;

		$controller_class_name = $this->Reg->Config->get('system', 'namespace', $application) . ucwords(strtolower($controller)) . 'ControllerClass';

		if(false == class_exists($controller_class_name))
		{
			throw new CoreException("Link to application '{$application}' and controller '{$controller}' could not be established. Controller class '{$controller_class_name}' is invalid.");
		}


		// Return an instance of the controller:

		return new $controller_class_name($this->Reg);
	}
}

?>